package com.evil.application.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.evil.application.constant.ApplicationConstant;
import com.evil.application.mapper.ApplicationGroupMapper;
import com.evil.application.mapper.ApplicationInfoMapper;
import com.evil.application.mapper.ApplicationPackageMapper;
import com.evil.application.mapper.PackageApplicationMapper;
import com.evil.application.pojo.dto.ApplicationReqDTO;
import com.evil.application.pojo.dto.ItemDTO;
import com.evil.application.pojo.dto.ReorderApplicationReqDTO;
import com.evil.application.pojo.entity.ApplicationGroup;
import com.evil.application.pojo.entity.ApplicationInfo;
import com.evil.application.pojo.entity.ApplicationPackage;
import com.evil.application.pojo.entity.PackageApplication;
import com.evil.application.service.PackageApplicationService;
import com.evil.common.application.dto.pkg.ModifyPackageApplicationSourceDTO;
import com.evil.common.application.dto.pkg.PackageApplicationSourceDTO;
import com.evil.common.application.dto.pkg.PackageApplicationSourcesDTO;
import com.evil.common.application.enums.ApplicationSourceTypeEnum;
import com.evil.common.core.enums.RCodeEnum;
import com.evil.common.core.enums.SwitchEnum;
import com.evil.common.core.exception.BusinessException;
import com.evil.common.core.util.StreamUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * 应用包应用表
 *
 * @author liyang
 * @date 2022-08-08 00:00
 */
@Slf4j
@RestController
@AllArgsConstructor
public class PackageApplicationServiceImpl extends ServiceImpl<PackageApplicationMapper, PackageApplication> implements PackageApplicationService {

    private final ApplicationPackageMapper applicationPackageMapper;

    private final ApplicationGroupMapper applicationGroupMapper;

    private final ApplicationInfoMapper applicationInfoMapper;

    private final ApplicationLogServiceImpl logService;

    /**
     * 获取应用包应用列表
     *
     * @param packageId packageId
     * @return list
     */
    @Override
    public List<PackageApplication> findByPackageId(Long packageId) {
        return this.baseMapper.findByPackageId(packageId);
    }

    /**
     * 修改应用/组挂载到应用包
     *
     * @param modifyPackageApplicationSourceDTO modifyPackageApplicationSourceDTO
     */
    @Override
    public void savePackageApplications(ModifyPackageApplicationSourceDTO modifyPackageApplicationSourceDTO) {
        Integer sourceType = modifyPackageApplicationSourceDTO.getSourceType();
        Long sourceId = modifyPackageApplicationSourceDTO.getSourceId();
        List<Long> packageIds = modifyPackageApplicationSourceDTO.getPackageIds();

        Long enterpriseId;
        if (ApplicationSourceTypeEnum.APPLICATION.getId() == sourceType) {
            enterpriseId = applicationInfoMapper.findById(sourceId).getEnterpriseId();
        } else if (ApplicationSourceTypeEnum.GROUP.getId() == sourceType) {
            enterpriseId = applicationGroupMapper.findById(sourceId).getEnterpriseId();
        } else {
            throw new BusinessException(RCodeEnum.ERROR_PARAMETER);
        }

        List<ApplicationPackage> packageList = applicationPackageMapper.findByIds(packageIds, true);
        if (packageList.size() != packageIds.size()) {
            throw new BusinessException(RCodeEnum.APPLICATION_PACKAGE_NOT_EXIST);
        }

        // 获取应用包应用列表（包括已删除）
        List<PackageApplication> packageApplicationList = this.findBySource(new PackageApplicationSourceDTO(modifyPackageApplicationSourceDTO));
        List<Long> dbApplicationPackageIds = StreamUtil.transListT(packageApplicationList, PackageApplication::getPackageId);

        packageApplicationList = packageApplicationList.stream().peek(e -> {
            if (!packageIds.contains(e.getPackageId())) {
                // 删除
                e.setIsDeleted(SwitchEnum.YES.getId());
            } else {
                // 未删除
                e.setIsDeleted(SwitchEnum.NO.getId());
            }
        }).collect(Collectors.toList());

        for (Long e : packageIds) {
            if (!dbApplicationPackageIds.contains(e)) {
                // 新增
                packageApplicationList.add(this.createPackageApplication(enterpriseId, sourceType, sourceId, e));
            }
        }

        logService.saveAndCheck(this.baseMapper, PackageApplication::getPackageApplicationId, packageApplicationList);
    }

    /**
     * 获取应用包应用列表
     *
     * @param sourceDTO sourceDTO
     * @return List
     */
    @Override
    public List<PackageApplication> findBySource(PackageApplicationSourceDTO sourceDTO) {
        return this.baseMapper.findBySource(sourceDTO);
    }

    /**
     * 获取应用包idsMap
     *
     * @param sourcesDTO sourcesDTO
     * @return List
     */
    @Override
    public Map<Long, List<Long>> findPackageIdsMapBySources(PackageApplicationSourcesDTO sourcesDTO) {
        return StreamUtil.groupAndThenTrans(this.baseMapper.findByPackageSources(sourcesDTO), PackageApplication::getPackageId, PackageApplication::getSourceId);
    }

    /**
     * 新增应用与包关联
     *
     * @param applicationPackage applicationPackage
     * @param applicationInfo    applicationInfo
     */
    @Override
    public void addPackageApp(ApplicationPackage applicationPackage, ApplicationInfo applicationInfo) {
        Long packageId = applicationPackage.getPackageId();
        Long enterpriseId = applicationInfo.getEnterpriseId();
        Long applicationId = applicationInfo.getApplicationId();
        int type = ApplicationSourceTypeEnum.APPLICATION.getId();

        PackageApplication packageApplication = this.baseMapper.findPackage(type, packageId, applicationId)
                .orElseGet(() -> this.createPackageApplication(enterpriseId, type, packageId, applicationId));
        packageApplication.setIsDeleted(SwitchEnum.NO.getId());
        logService.saveAndCheck(this.baseMapper, PackageApplication::getPackageApplicationId, packageApplication);
    }

    /**
     * 应用包应用列表
     *
     * @param applicationReqDTO applicationReqDTO
     * @return PackageApplication list
     */
    @Override
    public List<PackageApplication> findByApplicationReqDTO(ApplicationReqDTO applicationReqDTO) {
        return this.baseMapper.findByApplicationReqDTO(applicationReqDTO);
    }

    /**
     * 删除应用包应用
     *
     * @param sourceDTO sourceDTO
     */
    @Override
    public List<PackageApplication> deletePackageApplication(PackageApplicationSourceDTO sourceDTO) {
        List<PackageApplication> packageApplications = this.findBySource(sourceDTO);
        packageApplications.forEach(e -> e.setIsDeleted(SwitchEnum.YES.getId()));
        logService.saveAndCheck(this.baseMapper, PackageApplication::getPackageId, packageApplications);

        return packageApplications;
    }

    /**
     * 重新排序应用包应用
     *
     * @param reorderApplicationReqDTO reorderApplicationReqDTO
     */
    @Override
    public void reorderApplication(ReorderApplicationReqDTO reorderApplicationReqDTO) {
        Long packageId = reorderApplicationReqDTO.getPackageId();
        List<ItemDTO> items = reorderApplicationReqDTO.getItems();
        // 获取应用包应用
        Map<Integer, Map<Long, PackageApplication>> packageMap = this.findMapByPackageId(packageId);

        // 获取应用包应用列表
        Map<String, ApplicationInfo> appMap = applicationInfoMapper.findByIds(
                packageMap.get(ApplicationSourceTypeEnum.APPLICATION.getId()).keySet(), true).stream()
                .collect(Collectors.toMap(ApplicationInfo::getApplicationKey, e -> e));

        // 获取应用包应用组列表
        Map<Long, ApplicationGroup> groupMap = applicationGroupMapper.findByIds(
                packageMap.get(ApplicationSourceTypeEnum.GROUP.getId()).keySet(), true).stream()
                .collect(Collectors.toMap(ApplicationGroup::getApplicationGroupId, e -> e));

        AtomicInteger sort = new AtomicInteger(1);

        List<PackageApplication> packageApplications = new ArrayList<>();
        // 记录包内应用
        Consumer<PackageApplication> push = packageApplication -> {
            packageApplication.setSort(sort.getAndIncrement());
            packageApplications.add(packageApplication);
        };
        items.forEach(item -> {
            List<ApplicationInfo> groupApplications = new ArrayList<>();
            if (item.getIsGroup()) {
                // 应用组
                if (!groupMap.containsKey(item.getApplicationGroupId())) {
                    throw new BusinessException(RCodeEnum.APPLICATION_GROUP_NOT_EXIST);
                }
                // 记录包内应用
                push.accept(packageMap.get(ApplicationSourceTypeEnum.GROUP.getId()).get(item.getApplicationGroupId()));

                // 应用
                if (CollectionUtil.isNotEmpty(item.getSubItems())) {
                    groupApplications.addAll(item.getSubItems().stream().map(subItem -> {
                        if (subItem.getIsGroup()) {
                            throw new BusinessException(RCodeEnum.ERROR_PARAMETER, "参数错误，组内不允许再有分组");
                        }
                        if (!appMap.containsKey(subItem.getApplicationKey())) {
                            throw new BusinessException(RCodeEnum.APPLICATION_INFO_NOT_EXIST);
                        }
                        // 设置应用所在分组
                        ApplicationInfo applicationInfo = appMap.get(subItem.getApplicationKey());
                        // 记录包内应用
                        push.accept(packageMap.get(ApplicationSourceTypeEnum.APPLICATION.getId()).get(applicationInfo.getApplicationId()));

                        applicationInfo.setApplicationGroupId(item.getApplicationGroupId());
                        return applicationInfo;
                    }).collect(Collectors.toList()));
                }
            } else {
                // 应用
                if (!appMap.containsKey(item.getApplicationKey())) {
                    throw new BusinessException(RCodeEnum.APPLICATION_INFO_NOT_EXIST);
                }
                // 应用
                ApplicationInfo applicationInfo = appMap.get(item.getApplicationKey());
                applicationInfo.setApplicationGroupId(ApplicationConstant.DEFAULT_PARENT_APPLICATION_GROUP_ID);
                groupApplications.add(applicationInfo);
                // 记录包内应用
                push.accept(packageMap.get(ApplicationSourceTypeEnum.APPLICATION.getId()).get(applicationInfo.getApplicationId()));
            }

            // 更新应用所在组
            logService.saveAndCheck(applicationInfoMapper, ApplicationInfo::getApplicationId, groupApplications);
        });

        logService.saveAndCheck(this.baseMapper, PackageApplication::getPackageApplicationId, packageApplications);
    }

    /**
     * 获取应用包应用 map
     *
     * @param packageId packageId
     * @return Map
     */
    @Override
    public Map<Integer, Map<Long, PackageApplication>> findMapByPackageId(Long packageId) {
        // 获取应用包应用
        List<PackageApplication> packageApplications = this.findByPackageId(packageId);

        Map<Integer, Map<Long, PackageApplication>> packageMap = StreamUtil.groupAndThenTransMapK(
                packageApplications, PackageApplication::getSourceType, PackageApplication::getSourceId);
        if (!packageMap.containsKey(ApplicationSourceTypeEnum.APPLICATION.getId())) {
            packageMap.put(ApplicationSourceTypeEnum.APPLICATION.getId(), new HashMap<>());
        }
        if (!packageMap.containsKey(ApplicationSourceTypeEnum.GROUP.getId())) {
            packageMap.put(ApplicationSourceTypeEnum.GROUP.getId(), new HashMap<>());
        }
        return packageMap;
    }

    /**
     * 创建应用包应用
     *
     * @param enterpriseId enterpriseId
     * @param sourceType   sourceType
     * @param sourceId     sourceId
     * @param packageId    packageId
     * @return PackageApplication
     */
    private PackageApplication createPackageApplication(Long enterpriseId, Integer sourceType, Long sourceId, Long packageId) {
        PackageApplication packageApplication = new PackageApplication();
        packageApplication.setEnterpriseId(enterpriseId);
        packageApplication.setPackageId(packageId);
        packageApplication.setSourceType(sourceType);
        packageApplication.setSourceId(sourceId);
        int maxSort = this.baseMapper.findMaxSortByApplicationPackageId(packageId);
        packageApplication.setSort(maxSort + 1);

        return packageApplication;
    }
}
